#ifdef USBCON
#ifdef USBD_USE_CDC

#include "ch32v_def.h"
#include "usbd_def.h"
#include "usbd_if.h"
#include "usbd_cdc.h"
#include "usbd_cdc_if.h"
#include "usbd_desc.h"
#include "core_debug.h"

__IO uint32_t lineState = 0;
__IO uint32_t TxState;

uint16_t SetupReqLen;
uint8_t SetupReqCode;
uint8_t UsbConfig;

CDC_TransmitQueue_TypeDef TransmitQueue;
CDC_ReceiveQueue_TypeDef ReceiveQueue;

__attribute__((aligned(4))) UINT8 EP0_Databuf[64];      //ep0(64)
__attribute__((aligned(4))) UINT8 EP1_Databuf[64 + 64]; //ep1_out(64)+ep1_in(64)
__attribute__((aligned(4))) UINT8 EP2_Databuf[64 + 64]; //ep2_out(64)+ep2_in(64)

USBD_CDC_LineCodingTypeDef linecoding = {
    115200, // 波特率为115200
    0x00,   // 1停止位
    0x00,   // 无校验
    0x08    // 8数据位
};

void CDC_init(void)
{
    CDC_TransmitQueue_Init(&TransmitQueue);
    CDC_ReceiveQueue_Init(&ReceiveQueue);

    pEP0_RAM_Addr = EP0_Databuf;
    pEP1_RAM_Addr = EP1_Databuf;
    pEP2_RAM_Addr = EP2_Databuf;

    USBHD_ClockCmd(RCC_USBCLKSource_PLLCLK_1Div5, ENABLE);
    USB_DeviceInit();
    NVIC_EnableIRQ(USBHD_IRQn);

    TxState = 0;
}

void CDC_continue_transmit(void)
{

    uint16_t size = 0;
    if (TxState == 0)
    {
        uint8_t *buffer = CDC_TransmitQueue_ReadBlock(&TransmitQueue, &size);
        if (size > 0)
        {
            CDC_TransmitQueue_CommitRead(&TransmitQueue);
            // core_debug(">>>>>> [%d]\n", size, buffer);
            memcpy(pEP2_IN_DataBuf, buffer, size);
            TxState = 1;

            DevEP2_IN_Deal(size);
        }
    }
}

void CDC_DevTransProcess(void)
{
    uint16_t len;
    uint8_t chtype;
    uint8_t intflag, errflag = 0;

    intflag = R8_USB_INT_FG; //USB中断标志寄存器
    // core_debug(" CDC_DevTransProcess ------------- 0x%02X \n", intflag);
    if (intflag & RB_UIF_TRANSFER) //USB传输完成中断标志位
    {
        /** 
         * R8_USB_INT_ST：USB中断状态寄存器   
         * MASK_UIS_TOKEN：设备模式下，当前USB传输事务的令牌PID标识  
         * MASK_UIS_ENDP：设备模式下，当前USB传输事务的端点号
         */
        switch (R8_USB_INT_ST & MASK_UIS_TOKEN)
        {
        case UIS_TOKEN_SETUP:
        {
            R8_UEP0_CTRL = RB_UEP_R_TOG | RB_UEP_T_TOG | UEP_R_RES_ACK | UEP_T_RES_NAK;
            len = R8_USB_RX_LEN;
            //获取一系列描述符
            if (len == sizeof(USB_SETUP_REQ))
            {
                SetupReqLen = pSetupReqPak->wLength;
                SetupReqCode = pSetupReqPak->bRequest;
                chtype = pSetupReqPak->bRequestType;

                len = 0;
                errflag = 0;

                if ((pSetupReqPak->bRequestType & USB_REQ_TYP_MASK) != USB_REQ_TYP_STANDARD)
                {
                    switch (SetupReqCode)
                    {
                    case CDC_GET_LINE_CODING:
                    {
                        pEP0_DataBuf[0] = (uint8_t)(linecoding.bitrate);
                        pEP0_DataBuf[1] = (uint8_t)(linecoding.bitrate >> 8);
                        pEP0_DataBuf[2] = (uint8_t)(linecoding.bitrate >> 16);
                        pEP0_DataBuf[3] = (uint8_t)(linecoding.bitrate >> 24);
                        pEP0_DataBuf[4] = linecoding.format;
                        pEP0_DataBuf[5] = linecoding.paritytype;
                        pEP0_DataBuf[6] = linecoding.datatype;
                        break;
                    }
                    case CDC_SET_CONTROL_LINE_STATE:
                    {
                        lineState = (pSetupReqPak->wValue & 0x01) != 0;
                        break;
                    }
                    case CDC_SET_LINE_CODING:
                    {
                        linecoding.bitrate = (uint32_t)(pEP0_DataBuf[0] |
                                                        pEP0_DataBuf[1] << 8 |
                                                        pEP0_DataBuf[2] << 16 |
                                                        pEP0_DataBuf[3] << 24);
                        linecoding.format = pEP0_DataBuf[4];
                        linecoding.paritytype = pEP0_DataBuf[5];
                        linecoding.datatype = pEP0_DataBuf[6];
                        break;
                    }
                    default:
                        errflag = 0xFF;
                        break;
                    }
                }
                else
                {
                    //若是标准请求
                    switch (SetupReqCode)
                    {
                    case USB_GET_DESCRIPTOR:
                    {
                        uint8_t *desc;

                        switch (((pSetupReqPak->wValue) >> 8))
                        {
                        case USB_DESCR_TYP_DEVICE:
                        {
                            desc = USBD_Class_DeviceDescriptor(&len);
                            break;
                        }
                        case USB_DESCR_TYP_CONFIG:
                        {
                            desc = USBD_CDC_CfgFSDesc;
                            len = USB_CDC_CONFIG_DESC_SIZ;
                            break;
                        }
                        case USB_DESCR_TYP_STRING:
                        {
                            switch ((pSetupReqPak->wValue) & 0xff)
                            {
                            case 1:
                            {
                                desc = USBD_ManufacturerStrDescriptor(&len);
                                break;
                            }
                            case 2:
                            {
                                desc = USBD_Class_ProductStrDescriptor(&len);
                                break;
                            }
                            case 0:
                            {
                                desc = USBD_LangIDStrDescriptor(&len);
                                break;
                            }
                            case 3:
                            {
                                desc = USBD_SerialStrDescriptor(&len);
                                break;
                            }
                            default:
                                errflag = 0xff;
                                break;
                            }
                            break;
                        }
                        default:
                            errflag = 0xff;
                            break;
                        }

                        if (SetupReqLen > len)
                            SetupReqLen = len;

                        len = (SetupReqLen > USB_MAX_EP0_SIZE) ? USB_MAX_EP0_SIZE : SetupReqLen;
                        memcpy(pEP0_DataBuf, desc, len);
                        break;
                    }
                    case USB_SET_ADDRESS:
                        SetupReqLen = (pSetupReqPak->wValue) & 0xff;
                        break;
                    case USB_GET_CONFIGURATION:
                        pEP0_DataBuf[0] = UsbConfig;
                        if (SetupReqLen > 1)
                            SetupReqLen = 1;
                        break;
                    case USB_SET_CONFIGURATION:
                        UsbConfig = (pSetupReqPak->wValue) & 0xff;
                        break;
                    case USB_CLEAR_FEATURE:
                    {
                        if ((pSetupReqPak->bRequestType & 0x1F) == USB_REQ_RECIP_DEVICE)
                        {
                            if ((pSetupReqPak->wValue) == 0x01)
                            {
                                if (USBD_CDC_CfgFSDesc[7] & 0x20)
                                {
                                    // do notihng. or wakeup
                                }
                                else
                                {
                                    errflag = 0xFF;
                                }
                            }
                            else
                            {
                                errflag = 0xFF;
                            }
                        }
                        else if ((pSetupReqPak->bRequestType & USB_REQ_RECIP_MASK) == USB_REQ_RECIP_ENDP)
                        {
                            switch ((pSetupReqPak->wIndex) & 0xff)
                            {
                            case 0x82:
                                R8_UEP2_CTRL = (R8_UEP2_CTRL & ~(RB_UEP_T_TOG | MASK_UEP_T_RES)) | UEP_T_RES_NAK;
                                break;
                            case 0x02:
                                R8_UEP2_CTRL = (R8_UEP2_CTRL & ~(RB_UEP_R_TOG | MASK_UEP_R_RES)) | UEP_R_RES_ACK;
                                break;
                            case 0x81:
                                R8_UEP1_CTRL = (R8_UEP1_CTRL & ~(RB_UEP_T_TOG | MASK_UEP_T_RES)) | UEP_T_RES_NAK;
                                break;
                            case 0x01:
                                R8_UEP1_CTRL = (R8_UEP1_CTRL & ~(RB_UEP_R_TOG | MASK_UEP_R_RES)) | UEP_R_RES_ACK;
                                break;
                            default:
                                errflag = 0xFF;
                                break;
                            }
                        }
                        else
                        {
                            errflag = 0xFF;
                        }
                        break;
                    }
                    case USB_SET_FEATURE:
                    {
                        if ((pSetupReqPak->bRequestType & 0x1F) == USB_REQ_RECIP_DEVICE)
                        {
                            if ((pSetupReqPak->wValue) == 0x01)
                            {
                                if (USBD_CDC_CfgFSDesc[7] & 0x20)
                                {
                                    // do notihng. or wakeup
                                }
                                else
                                {
                                    errflag = 0xFF;
                                }
                            }
                            else
                            {
                                errflag = 0xFF;
                            }
                        }
                        else if ((pSetupReqPak->bRequestType & 0x1F) == USB_REQ_RECIP_ENDP)
                        {
                            if ((pSetupReqPak->wValue) == 0x00)
                            {
                                switch (pSetupReqPak->wIndex)
                                {
                                case 0x82:
                                    R8_UEP2_CTRL = (R8_UEP2_CTRL & (~RB_UEP_R_TOG)) | UEP_T_RES_STALL; /* 设置端点2 IN STALL */
                                    break;
                                case 0x02:
                                    R8_UEP2_CTRL = (R8_UEP2_CTRL & (~RB_UEP_R_TOG)) | UEP_R_RES_STALL; /* 设置端点2 OUT Stall */
                                    break;
                                case 0x81:
                                    R8_UEP1_CTRL = (R8_UEP1_CTRL & (~RB_UEP_R_TOG)) | UEP_T_RES_STALL; /* 设置端点1 IN STALL */
                                    break;
                                case 0x01:
                                    R8_UEP1_CTRL = (R8_UEP1_CTRL & (~RB_UEP_R_TOG)) | UEP_R_RES_STALL; /* 设置端点1 OUT Stall */
                                    break;
                                default:
                                    errflag = 0xFF; /* 操作失败 */
                                    break;
                                }
                            }
                            else
                            {
                                errflag = 0xFF;
                            }
                        }
                        else
                        {
                            errflag = 0xFF;
                        }
                        break;
                    }
                    case USB_GET_INTERFACE:
                        pEP0_DataBuf[0] = 0x00;
                        if (SetupReqLen > 1)
                            SetupReqLen = 1;
                        break;
                    case USB_GET_STATUS:
                        pEP0_DataBuf[0] = 0x00;
                        pEP0_DataBuf[1] = 0x00;
                        if (SetupReqLen > 2)
                            SetupReqLen = 2;
                        break;
                    default:
                        errflag = 0xff;
                        break;
                    }
                }
            }
            else
            {
                errflag = 0xFF;
            }

            if (errflag == 0xFF)
            {
                R8_UEP0_CTRL = RB_UEP_R_TOG | RB_UEP_T_TOG | UEP_R_RES_STALL | UEP_T_RES_STALL;
            }
            else
            {
                if (chtype & 0x80)
                {
                    len = (SetupReqLen > USB_MAX_EP0_SIZE) ? USB_MAX_EP0_SIZE : SetupReqLen;
                    SetupReqLen -= len;
                }
                else
                    len = 0;

                R8_UEP0_T_LEN = len;
                R8_UEP0_CTRL = RB_UEP_R_TOG | RB_UEP_T_TOG | UEP_R_RES_ACK | UEP_T_RES_ACK;
            }
            break;
        }
        case UIS_TOKEN_IN:
        {
            switch (R8_USB_INT_ST & (MASK_UIS_TOKEN | MASK_UIS_ENDP))
            {
            case UIS_TOKEN_IN:
            {
                switch (SetupReqCode)
                {
                case USB_GET_DESCRIPTOR:
                {
                    len = (SetupReqLen > USB_MAX_EP0_SIZE) ? USB_MAX_EP0_SIZE : SetupReqLen;
                    SetupReqLen -= len;
                    R8_UEP0_T_LEN = len;
                    R8_UEP0_CTRL ^= RB_UEP_T_TOG;
                    break;
                }
                case USB_SET_ADDRESS:
                {
                    R8_USB_DEV_AD = (R8_USB_DEV_AD & RB_UDA_GP_BIT) | SetupReqLen;
                    R8_UEP0_CTRL = UEP_R_RES_ACK | UEP_T_RES_NAK;
                    break;
                }
                default:
                {
                    R8_UEP0_T_LEN = 0;
                    R8_UEP0_CTRL = UEP_R_RES_ACK | UEP_T_RES_NAK;
                    break;
                }
                }
                break;
            }
            case UIS_TOKEN_IN | 1:
            {
                R8_UEP1_CTRL ^= RB_UEP_T_TOG;
                R8_UEP1_CTRL = (R8_UEP1_CTRL & ~MASK_UEP_T_RES) | UEP_T_RES_NAK;
                break;
            }
            case UIS_TOKEN_IN | 2:
            {
                R8_UEP2_CTRL ^= RB_UEP_T_TOG;
                R8_UEP2_CTRL = (R8_UEP2_CTRL & ~MASK_UEP_T_RES) | UEP_T_RES_NAK;
                TxState = 0;
                CDC_continue_transmit();

                break;
            }
            default:
                break;
            }
            break;
        }
        case UIS_TOKEN_OUT:
        {
            switch (R8_USB_INT_ST & (MASK_UIS_TOKEN | MASK_UIS_ENDP))
            {
            case UIS_TOKEN_OUT:
            {
                if (SetupReqCode == CDC_SET_LINE_CODING)
                {
                    if (R8_USB_INT_FG & RB_U_TOG_OK)
                    {
                        R8_UEP0_T_LEN = 0;
                        R8_UEP0_CTRL |= UEP_R_RES_ACK | UEP_T_RES_ACK; // 准备上传0包
                    }
                }
                else
                {
                    R8_UEP0_T_LEN = 0;
                    R8_UEP0_CTRL |= UEP_R_RES_ACK | UEP_T_RES_NAK; //状态阶段，对IN响应NAK
                }
                break;
            }
            case UIS_TOKEN_OUT | 1:
            {
                if (R8_USB_INT_ST & RB_UIS_TOG_OK)
                {
                    R8_UEP1_CTRL ^= RB_UEP_R_TOG;
                    len = R8_USB_RX_LEN;
                    DevEP1_OUT_Deal(len);
                }
                break;
            }
            case UIS_TOKEN_OUT | 2:
            {
                if (R8_USB_INT_ST & RB_UIS_TOG_OK)
                {
                    R8_UEP2_CTRL ^= RB_UEP_R_TOG;
                    len = R8_USB_RX_LEN;
                    DevEP2_OUT_Deal(len);
                }
                break;
            }
            default:
                break;
            }
            break;
        }
        case UIS_TOKEN_SOF:
            break;
        default:
            break;
        }

        R8_USB_INT_FG = RB_UIF_TRANSFER;
    }
    else if (intflag & RB_UIF_BUS_RST)
    {
        R8_USB_DEV_AD = 0;
        R8_UEP0_CTRL = UEP_R_RES_ACK | UEP_T_RES_NAK;
        R8_UEP1_CTRL = UEP_R_RES_ACK | UEP_T_RES_NAK;
        R8_UEP2_CTRL = UEP_R_RES_ACK | UEP_T_RES_NAK;
        R8_USB_INT_FG |= RB_UIF_BUS_RST;
    }
    else if (intflag & RB_UIF_SUSPEND)
    {
        if (R8_USB_MIS_ST & RB_UMS_SUSPEND)
        {
            ;
        }
        else
        {
            ;
        }
        R8_USB_INT_FG = RB_UIF_SUSPEND;
    }
    else
    {
        R8_USB_INT_FG = intflag;
    }
}

void DevEP1_OUT_Deal(UINT8 l)
{
}

void DevEP2_OUT_Deal(UINT8 l)
{
    uint8_t *block = CDC_ReceiveQueue_ReserveBlock(&ReceiveQueue);
    if (block != NULL)
    {
        for (int i = 0; i < l; i++)
        {
            block[i] = pEP2_OUT_DataBuf[i];
        }

        CDC_ReceiveQueue_CommitBlock(&ReceiveQueue, l);
    }
}

#endif /* USBD_USE_CDC */
#endif /* USBCON */
